package com.wst.system.interceptor.encrypt;

import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wst.system.config.SecretKeyComponent;
import com.wst.system.config.cache.SysCache;
import com.wst.system.config.cache.SysCacheServer;
import com.wst.system.config.exception.GlobalExcetion;
import com.wst.system.util.CryptoContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

@Slf4j
@Aspect
@Component
public class EncryptDecryptAspect {

    @Autowired
    private SecretKeyComponent secretKeyComponent;

    @Autowired
    private SysCacheServer sysCacheServer;

    // 环绕通知，拦截带有 @EncryptDecrypt 注解的方法
    @Around("@annotation(EncryptDecrypt)")
    public Object handleEncryptDecrypt(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        String reqStr=req.getParameter("reqStr");
        if(!StringUtils.isNotBlank(reqStr)){
            throw new GlobalExcetion(1,"接口请求参数不得为空");
        }
        String sessionId=req.getParameter("sessionId");
        if(!StringUtils.isNotBlank(sessionId)){
            throw new GlobalExcetion(1,"接口会话id不得为空");
        }
        SysCache sysCache=sysCacheServer.getOnce(sessionId);
        if(sysCache==null){
            throw new GlobalExcetion(100,"接口会话id已过期, 请刷新系统");
        }
        String secretKey=secretKeyComponent.getSecretKey(sysCache.getValue().toString());
        System.out.println(secretKey);
        String decryptStr=CryptoContextUtils.decryptAES(reqStr,secretKey);
        log.info("解密str:{}",decryptStr);
        // 获取方法名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        EncryptDecrypt encryptDecrypt=method.getAnnotation(EncryptDecrypt.class);
        // 获取方法参数
        Object[] args = joinPoint.getArgs();
        // 解密请求参数
        if (args != null && args.length > 0 ) {
            for (int i = 0; i < args.length; i++) {
                if(args[i] instanceof HttpServletRequest){
                    args[i]=req;
                }else if(args[i] instanceof HttpServletResponse){
                    args[i]=resp;
                }else if(args[i] instanceof JSONObject){
                    args[i]=JSONObject.parseObject(decryptStr);
                } else if(args[i] instanceof MultipartFile) {
                    throw new GlobalExcetion(1,"MultipartFile 暂不支持");
                }else {
                    args[i]=JSONObject.parseObject(decryptStr,args[i].getClass());
                    EncryptAspectCommon.dealParamValited(args[i]);
                }
            }
        }
        // 执行目标方法
        Object result = joinPoint.proceed(args);
        if(result instanceof JSONObject){
            JSONObject jsonObject=(JSONObject)result;
            if(jsonObject.containsKey("data")){
                Object data=jsonObject.get("data");
                String respStr=JSONObject.toJSONString(data);
                respStr=CryptoContextUtils.encryptAES(respStr,secretKey);
                jsonObject.put("data",respStr);
            }
        }
        return result;
    }


}
